/**
* Copyright (c) By zengqh.
*
* This program is just for fun or demo, in the hope that it  
* will be useful, you can redistribute it and/or modify freely.
*
* Time: 2013/03/30
* File: ProgramGen.cpp
* Blog: http://www.cnblogs.com/zengqh/
**/

#include "ProgramGen.h"
#include "RenderSystem.h"

namespace HY
{
ProgramGen::ProgramGen(const std::string& vs_str, const std::string& ps_str)
: vs_str_(vs_str)
, ps_str_(ps_str)
{

}

ProgramGen::~ProgramGen()
{

}

const std::string& ProgramGen::getVSStr() const
{
	return vs_str_;
}

const std::string& ProgramGen::getPSStr() const
{
	return ps_str_;
}

ProgramGen::GeneratedProgram* ProgramGen::getProgram(const ProgramGen::Flags& flags)
{
	uint32 vs_flags = flags.getVSFlags();
	uint32 ps_flags = flags.getPSFlags();
	uint64 pro_id = vs_flags + ((uint64)(ps_flags) << 32);

	ProgramGen::GeneratedProgram* gen_pro = findProgram(flags);

	if (gen_pro)
	{
		return gen_pro;
	}

	gen_pro = new ProgramGen::GeneratedProgram;

	Program* pro = rc_getRenderSys()->createProgram();
	HY_ASSERT(pro);

	VertexShader* vs = findVS(flags);
	PixelShader* ps = findPS(flags);

	if (!vs)
	{
		vs = createVS(flags);
	}

	if (!ps)
	{
		ps = createPS(flags);
	}

	gen_pro->pro = pro;
	gen_pro->vs_flags = vs_flags;
	gen_pro->ps_flags = ps_flags;
	gen_pro->user_data = 0;

	pro_list_.push_back(gen_pro);
	pro_map_[pro_id] = gen_pro;

	return gen_pro;
}

VertexShader* ProgramGen::findVS(const ProgramGen::Flags& flags)
{
	uint32 vs_flags = flags.getVSFlags();

	std::map<uint32, VertexShader*>::iterator iter = vs_map_.find(vs_flags);
	if (iter != vs_map_.end())
	{
		return iter->second;
	}

	return 0;
}

PixelShader* ProgramGen::findPS(const Flags& flags)
{
	uint32 ps_flags = flags.getPSFlags();

	std::map<uint32, PixelShader*>::iterator iter = ps_map_.find(ps_flags);
	if (iter != ps_map_.end())
	{
		return iter->second;
	}

	return 0;
}

ProgramGen::GeneratedProgram* ProgramGen::findProgram(const ProgramGen::Flags& flags)
{
	uint32 vs_flags = flags.getVSFlags();
	uint32 ps_flags = flags.getPSFlags();

	uint64 pro_id = vs_flags + ((uint64)(ps_flags) << 32);

	std::map<uint64, ProgramGen::GeneratedProgram*>::iterator iter = pro_map_.find(pro_id);

	if (iter != pro_map_.end())
	{
		return iter->second;
	}

	return 0;
}

VertexShader* ProgramGen::createVS(const ProgramGen::Flags& flags)
{
	uint32 vs_flags = flags.getVSFlags();

	VertexShader* vs = rc_getRenderSys()->createVertexShader();

	HY_ASSERT(vs);

	std::string source_code_str;
	const std::vector<std::string>& vs_defs = flags.getVSDefs();
	for (uint32 i = 0; i < vs_defs.size(); ++i)
	{
		source_code_str += "#define ";
		source_code_str += vs_defs[i];
		source_code_str += "\n";
	}

	source_code_str += vs_str_;

	vs->setSourceCode(source_code_str);

	vs_list_.push_back(vs);

	vs_map_[vs_flags] = vs;

	return vs;
}

PixelShader* ProgramGen::createPS(const ProgramGen::Flags& flags)
{
	uint32 ps_flags = flags.getPSFlags();

	PixelShader* ps = rc_getRenderSys()->createPixelShader();

	HY_ASSERT(ps);

	std::string source_code_str;
	const std::vector<std::string> ps_defs = flags.getPSDefs();
	for (uint32 i = 0; i < ps_defs.size(); ++i)
	{
		source_code_str += "#define ";
		source_code_str += ps_defs[i];
		source_code_str += "\n";
	}

	source_code_str += ps_str_;

	ps->setSourceCode(source_code_str);

	ps_list_.push_back(ps);
	ps_map_[ps_flags] = ps;

	return ps;
}

}